/*
 * Copyright (c) 1996, 2012, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */

package java.io;


/**
 * 用于读取字符流的抽象类。子类必须实现的唯一方法是read(char[]， int, int)和close()。
 * 然而，大多数子类将重写这里定义的一些方法，以提供更高的效率、额外的功能，或者同时提供这两种功能。
 *
 *
 * @see BufferedReader
 * @see   LineNumberReader
 * @see CharArrayReader
 * @see InputStreamReader
 * @see   FileReader
 * @see FilterReader
 * @see   PushbackReader
 * @see PipedReader
 * @see StringReader
 * @see Writer
 *
 * @author      Mark Reinhold
 * @since       JDK1.1
 */

public abstract class Reader implements Readable, Closeable {

    /**
     * 用于同步此流上的操作的对象。
     * 为了高效，字符流对象可以使用其他对象来保护临界区。
     * 因此，子类应该使用该对象，而不是用this或者synchronized方法。
     */
    protected Object lock;

    /**
     * 创建一个新的字符流读取器，它的临界区是读取器自己。
     */
    protected Reader() {
        this.lock = this;
    }

    /**
     * 创建一个新的字符流读取器，它的临界区是给定的对象。
     *
     * @param lock  The Object to synchronize on.
     */
    protected Reader(Object lock) {
        if (lock == null) {
            throw new NullPointerException();
        }
        this.lock = lock;
    }

    /**
     * 尝试将字符读入指定的字符缓冲区。
     * 缓冲区用作字符存储库:
     * 所做的更改仅是put操作的结果。
     * 不执行缓冲区的flip或者rewind。
     *
     * @param target the buffer to read characters into
     * @return The number of characters added to the buffer, or
     *         -1 if this source of characters is at its end
     * @throws IOException if an I/O error occurs
     * @throws NullPointerException if target is null
     * @throws java.nio.ReadOnlyBufferException if target is a read only buffer
     * @since 1.5
     */
    public int read(java.nio.CharBuffer target) throws IOException {
    	// 创建一个char数组，长度为target剩余的空间
        int len = target.remaining();
        char[] cbuf = new char[len];
        // 调用read方法，填入cbuf的0，长度为len
        int n = read(cbuf, 0, len);
        // 如果读取到了数据，从cbuf往target填充数据
        if (n > 0)
            target.put(cbuf, 0, n);
        // 返回读取到的char数量
        return n;
    }

    /**
     * 读取单个字符。此方法将阻塞，直到出现可用字符、发生I/O错误或到达流的末尾为止。
     *
     * <p>想要支持高效单字符输入的子类应该重写这个方法。
     *
     * @return     The character read, as an integer in the range 0 to 65535
     *             (<tt>0x00-0xffff</tt>), or -1 if the end of the stream has
     *             been reached
     *
     * @exception  IOException  If an I/O error occurs
     */
    public int read() throws IOException {
        char cb[] = new char[1];
        if (read(cb, 0, 1) == -1)
            return -1;
        else
            return cb[0];
    }

    /**
     * 将字符读入数组。
     * 此方法将阻塞，直到一些可用的输入、发生I/O错误或到达流的末尾为止。
     *
     * @param       cbuf  Destination buffer
     *
     * @return      The number of characters read, or -1
     *              if the end of the stream
     *              has been reached
     *
     * @exception   IOException  If an I/O error occurs
     */
    public int read(char cbuf[]) throws IOException {
        return read(cbuf, 0, cbuf.length);
    }

    /**
     * 将字符读入数组的一部分。
     * 这个方法将被阻塞，直到有一些输入可用、出现I/O错误或到达流的末尾。
     * 注意：该方法一定要实现
     *
     * @param      cbuf  Destination buffer
     * @param      off   Offset at which to start storing characters
     * @param      len   Maximum number of characters to read
     *
     * @return     The number of characters read, or -1 if the end of the
     *             stream has been reached
     *
     * @exception  IOException  If an I/O error occurs
     */
    abstract public int read(char cbuf[], int off, int len) throws IOException;

    /** 最大跳跃缓存大小 8192*/
    private static final int maxSkipBufferSize = 8192;

    /** 跳跃缓冲区，null直到分配*/
    private char skipBuffer[] = null;

    /**
     * 跳过字符。此方法将阻塞，直到出现一些可用字符、发生I/O错误或到达流的末尾为止。
     *
     * @param  n  跳过的字符数目
     *
     * @return    The number of characters actually skipped
     *
     * @exception  IllegalArgumentException  If <code>n</code> is negative.
     * @exception  IOException  If an I/O error occurs
     */
    public long skip(long n) throws IOException {
        if (n < 0L)
            throw new IllegalArgumentException("skip value is negative");
        // 一次跳过min(n,maxSkipBufferSize)，如果没有调到n，就多跳几次
        int nn = (int) Math.min(n, maxSkipBufferSize);
        synchronized (lock) {
            if ((skipBuffer == null) || (skipBuffer.length < nn))
                skipBuffer = new char[nn];
            long r = n;
            // r为还要跳过的字符数
            while (r > 0) {
            	// 通过调用read方法，读入skipBuffer来跳过
                int nc = read(skipBuffer, 0, (int)Math.min(r, nn));
                // 如果某次读取数为-1，跳出循环
                if (nc == -1)
                    break;
                r -= nc;
            }
            return n - r;
        }
    }

    /**
     * 告知该流是否已准备好读取。
     *
     * @return 如果保证下一个read()不会阻塞输入，则为True，否则为false。
     * 注意，返回false并不保证下一个读取将阻塞。
     *
     * @exception  IOException  If an I/O error occurs
     */
    public boolean ready() throws IOException {
        return false;
    }

    /**
     * 告知此流是否支持mark()操作。结果总是返回false。子类应该重写这个方法。
     *
     * @return true if and only if this stream supports the mark operation.
     */
    public boolean markSupported() {
        return false;
    }

    /**
     * 标记流中的当前位置。对reset()的后续调用将尝试将流重新定位到此处。
     * 并非所有字符输入流都支持mark()操作。
     *
     * @param  readAheadLimit  在保留标记的同时限制可能丢失的字符的数量。
     *                         在读取了这么多字符之后，尝试重置流可能会失败。
     *
     * @exception  IOException  If the stream does not support mark(),
     *                          or if some other I/O error occurs
     */
    public void mark(int readAheadLimit) throws IOException {
        throw new IOException("mark() not supported");
    }

    /**
     * 重置。如果流已被标记，则尝试在标记处重新定位它。
     * 如果流没有被标记，那么尝试以某种适合于特定流的方式重置它，例如将其重新定位到起始点。
     * 不是所有字符输入流都支持reset()操作，而且有些支持reset()不支持mark()。
     *
     * @exception  IOException  If the stream has not been marked,
     *                          or if the mark has been invalidated,
     *                          or if the stream does not support reset(),
     *                          or if some other I/O error occurs
     */
    public void reset() throws IOException {
        throw new IOException("reset() not supported");
    }

    /**
     * 关闭流并释放与之关联的任何系统资源。
     * 一旦流被关闭，进一步的read()、ready()、mark()、reset()或skip()调用将抛出一个IOException。
     * 关闭以前关闭的流没有任何效果。
     *
     * @exception  IOException  If an I/O error occurs
     */
     abstract public void close() throws IOException;

}
